package com.king.asm;

import java.io.FileOutputStream;
import java.lang.reflect.Method;

import org.objectweb.asm.*;

public class AsmAopTest extends ClassLoader implements Opcodes {

	public static void main(String[] args) throws Exception {
		ClassReader cr = new ClassReader(Foo.class.getName());
		ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
		ClassVisitor cv = new MethodChangeClassAdapter(cw);
		cr.accept(cv, Opcodes.ASM4);

		byte[] code = cw.toByteArray();
		AsmAopTest loader = new AsmAopTest();
		Class<?> exampleClass = loader.defineClass(Foo.class.getName(), code, 0, code.length);

		for (Method method : exampleClass.getMethods()) {
			System.out.println(method);
		}

		exampleClass.getMethods()[0].invoke(null, null);  // 調用execute，修改方法內容

		FileOutputStream fos = new FileOutputStream("/home/taomk/king-training/class-loader/src/main/java/com/king/asm/Example.class");
		fos.write(code);
		fos.close();
	}

	static class AsmMethodVisit extends MethodVisitor {
		public AsmMethodVisit(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}

		@Override
		public void visitCode() {
			//此方法在访问方法的头部时被访问到，仅被访问一次
			visitMethodInsn(Opcodes.INVOKESTATIC, "com/king/asm/Monitor", "start", "()V");
			super.visitCode();
		}

		@Override
		public void visitInsn(int opcode) {
			//此方法可以获取方法中每一条指令的操作类型，被访问多次
			//如应在方法结尾处添加新指令，则应判断：
			if (opcode == Opcodes.RETURN) {
				visitMethodInsn(Opcodes.INVOKESTATIC, "com/king/asm/Monitor", "end", "()V");
			}
			super.visitInsn(opcode);
		}
	}

	static class MethodChangeClassAdapter extends ClassVisitor implements Opcodes {

		public MethodChangeClassAdapter(final ClassVisitor cv) {
			super(Opcodes.ASM4, cv);
		}

		@Override
		public MethodVisitor visitMethod(
				int access,
				String name,
				String desc,
				String signature,
				String[] exceptions) {
			if ("execute".equals(name))  // 此处的execute即为需要修改的方法，修改方法內容
			{
				MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);//先得到原始的方法
				return new AsmMethodVisit(mv); // 访问需要修改的方法
			}

			return null;
		}
	}
}